package com.weds.data_sync.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.weds.data_sync.advice.exception.BizCodeException;
import com.weds.data_sync.constant.BizCodeEnum;
import com.weds.data_sync.constant.ScriptServiceConstant;
import com.weds.data_sync.mapper.DataSyncScriptGroupMapper;
import com.weds.data_sync.mapper.DataSyncScriptMapper;
import com.weds.data_sync.mapper.DataSyncScriptPlanMapper;
import com.weds.data_sync.mapper.DataSyncScriptRelationMapper;
import com.weds.data_sync.pojo.dto.PageDto;
import com.weds.data_sync.pojo.dto.ScriptDto;
import com.weds.data_sync.pojo.entity.DataSyncScriptDO;
import com.weds.data_sync.pojo.entity.DataSyncScriptGroupDO;
import com.weds.data_sync.pojo.entity.DataSyncScriptPlanDO;
import com.weds.data_sync.pojo.entity.DataSyncScriptRelationDO;
import com.weds.data_sync.service.ScriptService;
import com.weds.data_sync.util.CommonUtil;
import com.weds.data_sync.util.JsonData;
import com.weds.data_sync.util.ScriptUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.Base64;
import java.util.List;
import java.util.Objects;

/**
 * <p>
 * 脚本列表 服务实现类
 * </p>
 *
 * @author lhw
 * @since 2023-12-29
 */
@Slf4j
@Service
public class ScriptServiceImp extends ServiceImpl<DataSyncScriptMapper, DataSyncScriptDO> implements ScriptService {

    @Autowired
    private DataSyncScriptMapper scriptMapper;

    @Autowired
    private DataSyncScriptPlanMapper planMapper;

    @Autowired
    private DataSyncScriptGroupMapper groupMapper;

    @Autowired
    private DataSyncScriptRelationMapper relationMapper;

    @Transactional
    @Override
    public Integer add(ScriptDto scriptDto) {
        DataSyncScriptDO dataSyncScriptDO = new DataSyncScriptDO();
        BeanUtils.copyProperties(scriptDto, dataSyncScriptDO);
        // 存储文件,将base64编码字符串转换成byte数组
        if (StringUtils.isNotBlank(scriptDto.getScriptFileBase64Str())) {
            String fileEncodeStr = scriptDto.getScriptFileBase64Str().replace(ScriptServiceConstant.WEB_BASE64_PREFIX, "");
            if (scriptDto.getSaveType().equals(ScriptServiceConstant.FileSaveType.FILE)) {
                byte[] fileByteArr = Base64.getDecoder().decode(fileEncodeStr);
                dataSyncScriptDO.setScriptFile(fileByteArr);
            }
        } else {
            dataSyncScriptDO.setScriptFile(null);
        }

        // 判断脚本分组信息并插入
        if (Objects.isNull(scriptDto.getGroupId())) {
            DataSyncScriptGroupDO groupDO = groupMapper.selectOne(new QueryWrapper<DataSyncScriptGroupDO>()
                    .eq("is_default", 1)
                    .eq("use_status_id", 1)
                    .eq("is_delete", 0));
            scriptDto.setGroupId(groupDO.getId());
        }

        if (Objects.isNull(scriptDto.getIsDefault())) {
            scriptDto.setIsDefault(0);
        }

        int scriptRow = scriptMapper.insert(dataSyncScriptDO);
        log.info("插入脚本: {}", dataSyncScriptDO);
        DataSyncScriptRelationDO groupRelation = DataSyncScriptRelationDO.builder()
                .scriptId(dataSyncScriptDO.getId())
                .groupId(scriptDto.getGroupId())
                .isDefault(scriptDto.getIsDefault())
                .build();
        int updateDefaultScript = relationMapper.update(new UpdateWrapper<DataSyncScriptRelationDO>()
                .eq("group_id", scriptDto.getGroupId())
                .set("is_default", 0));
        log.info("还原默认分组");
        int relationRow = relationMapper.insert(groupRelation);
        log.info("插入脚本分组: {}", groupRelation);

        return scriptRow;
    }

    @Override
    public Integer del(List<Long> ids) {
        if (Objects.isNull(ids) || ids.isEmpty()) {
            throw new BizCodeException(BizCodeEnum.SCRIPT_DEL_NOCHOOSE);
        }
        // 判断脚本是否在执行方案中
        Boolean useFlag = judgeScriptInUse(ids);
        if (useFlag) {
            throw new BizCodeException(BizCodeEnum.SCRIPT_DEL_USED);
        }

        return scriptMapper.deleteBatchIds(ids);
    }

    @Override
    public Integer update(ScriptDto scriptDto) {
        if (Objects.isNull(scriptDto.getId())) {
            throw new BizCodeException(BizCodeEnum.SCRIPT_UPDATE_UNKNOW);
        }
        DataSyncScriptDO dataSyncScriptDO = new DataSyncScriptDO();
        BeanUtils.copyProperties(scriptDto, dataSyncScriptDO);
        // 存储文件,将base64编码字符串转换成byte数组
        if (StringUtils.isNotBlank(scriptDto.getScriptFileBase64Str())) {
            String fileEncodeStr = scriptDto.getScriptFileBase64Str().replace(ScriptServiceConstant.WEB_BASE64_PREFIX, "");
            if (scriptDto.getSaveType().equals(ScriptServiceConstant.FileSaveType.FILE)) {
                byte[] fileByteArr = Base64.getDecoder().decode(fileEncodeStr);
                dataSyncScriptDO.setScriptFile(fileByteArr);
            }
        }
        return scriptMapper.updateById(dataSyncScriptDO);
    }

    @Override
    public JsonData.PageData<DataSyncScriptDO> queryData(PageDto pageDto) {
        Page<DataSyncScriptDO> pageInfo = new Page<>(pageDto.getPage(), pageDto.getSize());
        scriptMapper.selectPage(pageInfo, new QueryWrapper<DataSyncScriptDO>()
                .eq("use_status_id", 1)
                .eq("is_delete", 0)
                .orderByAsc("id"));
        return CommonUtil.pageToDto(pageInfo);
    }

    @Override
    public void runScriptNativeById(Long scriptId) throws IOException {
        DataSyncScriptDO scriptDO = scriptMapper.selectById(scriptId);
        if (Objects.isNull(scriptDO)) {
            throw new BizCodeException(BizCodeEnum.SCRIPT_EXEC_UNKNOW);
        }
        runScriptNative(scriptDO);
    }

    @Override
    public void runScriptNative(DataSyncScriptDO scriptDO) throws IOException {
        String sourceContext = "python";
        if (StringUtils.isNotBlank(scriptDO.getSourceContext())) {
            sourceContext = scriptDO.getSourceContext();
        }

        ScriptUtil.runScriptString(scriptDO.getSourceCode(), new String[]{sourceContext, scriptDO.getPrefixCmd()},
                new String[]{scriptDO.getSuffixCmd()},
                ScriptServiceConstant.ScriptType.getFileSuffixByScriptType(scriptDO.getSourceType()));
    }

    @Deprecated
    @Async
    @Override
    public void runScriptNativeAsync(DataSyncScriptDO scriptDO) throws IOException {
        String sourceContext = "python";
        if (StringUtils.isNotBlank(scriptDO.getSourceContext())) {
            sourceContext = scriptDO.getSourceContext();
        }

        ScriptUtil.runScriptString(scriptDO.getSourceCode(), new String[]{sourceContext, scriptDO.getPrefixCmd()},
                new String[]{scriptDO.getSuffixCmd()},
                ScriptServiceConstant.ScriptType.getFileSuffixByScriptType(scriptDO.getSourceType()));
    }


    /**
     * 判断某个脚本是否被执行方案使用
     *
     * @param scriptIds
     * @return
     */
    public Boolean judgeScriptInUse(List<Long> scriptIds) {
        List<DataSyncScriptPlanDO> planList = planMapper.selectList(new QueryWrapper<DataSyncScriptPlanDO>()
                .eq("use_status_id", 1)
                .eq("is_delete", 0));
        for (DataSyncScriptPlanDO planItem : planList) {
            List<Integer> scriptArr = planItem.getScriptArr();
            for (Long scriptId : scriptIds) {
                if (scriptArr.contains(scriptId.intValue())) {
                    return true;
                }
            }

        }
        return false;
    }
}
